/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package uk.ac.bath.machines.neuralSane;

import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;

import uk.ac.bath.environment.MachineIF;
import uk.ac.bath.legacy.Value;

/**
 *
 * @author pjl
 */
public final class Network implements MachineIF, Cloneable {

    final Value x[];
    final Neuron neurons[];
    final int nIn;
    final int nHidden;
    final int nOut;
    private int nState;
    private int nNeuron;
    private boolean recursive = false;
    private int nUserIn;

    /**
     *
     *
     * @param nUserIn     number of real inputs
     * @param nHidden     number of hidden neurons
     * @param nOut        number of output neurons
     * @param recursive   add extra inputs that get there state from the hidden layer before each tick
     */
    public Network(int nUserIn, int nHidden, int nOut, boolean recursive) {

        this.nUserIn = nUserIn;
        this.recursive = recursive;

        if (recursive) {
            this.nIn = nUserIn + nHidden;
        } else {
            this.nIn = nUserIn;
        }

        this.nHidden = nHidden;
        this.nOut = nOut;
        this.nState = nIn + nHidden + nOut;
        this.nNeuron = nHidden + nOut;

        // state vector
        x = new Value[nState];
        for (int i = 0; i < nState; i++) {
            x[i] = new Value(0.0);
        }

        neurons = new Neuron[nNeuron];
    }

    /**
     * Helper methods for constructing connections.
     *
     * Create hidden neuron with input connections
     * 
     * @param ptr
     * @param wght
     * @param id
     */
    public void addHidden(ArrayList<Integer> ptr, ArrayList<Float> wght, int iHid) {
        int n = ptr.size();

        Value inPack[] = new Value[n];
        Float w[] = new Float[n];
        for (int i = 0; i < n; i++) {
            inPack[i] = x[ptr.get(i)];
            w[i] = wght.get(i);

        }
        neurons[iHid] = new Neuron(inPack, w, x[nIn + iHid]);
    }

    /**
     *  create output neuron
     *
     * @param ptr    ptr into hidden layer
     * @param wgt    weight vector
     * @param iOut   output number
     */
    public void addOut(ArrayList<Integer> ptr, ArrayList<Float> wgt, int iOut) {
        int n = ptr.size();
        assert (n == wgt.size());
        Value inPack[] = new Value[n];
        Float w[] = new Float[n];

        for (int i = 0; i < n; i++) {
            inPack[i] = x[ptr.get(i) + nIn];
            w[i] = wgt.get(i);
            assert (w[i] != null);

        }

        neurons[nHidden + iOut] = new Neuron(inPack, w, x[nIn + nHidden + iOut]);

    }

    public int getTotalIn() {
        return nIn;
    }

    public boolean isOn(int i) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public float out(int i) {
        return (float) x[nIn + nHidden + i].val;
    }

    public void setIn(int i, float fVal) {
        assert (i < nUserIn);
        x[i].val = fVal;
    }

    public int size() {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public void tick() {

        if (recursive) {
            for (int i = 0; i < nHidden; i++) {
                x[i + nUserIn].val = x[i + nIn].val;
            }
        }

        for (final Neuron n : neurons) {
            n.fire();
        }
    }

    public Object clone() throws CloneNotSupportedException {
        throw new CloneNotSupportedException();
    }

    public int sizeIn() {
        return nUserIn;
    }

    public int sizeOut() {
        return nOut;
    }

    public void toString(StringWriter w) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    /**
     * Only valid for vinalla networks (no sparsity)
     * @return
     */
    public String parametersToString() {
        String ret = "";

        for (int i = 0; i < nHidden; i++) {
            Neuron n = neurons[i];
            for (float wi : n.w) {
                ret = ret + " " + wi;
            }

            for (int o = 0; o < nOut; o++) {
                Neuron on = neurons[nHidden+o];
                ret = ret + " " + on.w[i];
            }

        }

        return ret;
    }
}
